home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
10,000 Great Games
/
10,000 Great Games.iso
/
Product
/
66
/
data1.cab
/
Source_Files
/
Src
/
Grav.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2000-01-16
|
9KB
|
469 lines
/* This file contains gravity effect routines
Data format of gravity_map is:
short 'width'
short 'height'
height times:
{ int 'offset to scanline from here' }
height times:
{
for a line containing nothing:
{
short 'width'
}
or for a line containing something:
{
short 'leading zeros'
short 'writes'
writes times: short 'offset of source pixel'
}
}
*/
#include "stdafx.h"
#include <math.h>
static short *radial_gravity(int maxr, double a, double t, double f(double r, double a, double t), int &n)
{
short w = 2 * maxr - 1, *gravity_map = new short [2 + 3 * w + square(w)], *g = gravity_map;
// Store size of gravity map
*g++ = w;
*g++ = w;
// Make room for table to store line offsets
int *current_line = (int *)g;
g = (short *)¤t_line[w];
// Compute the gravity map
for (int y = -maxr + 1; y < maxr; y++)
{
short lead_zeros = 0, writes = 0, *where_writes, trail_zeros = 0, non_zero = FALSE;
int offset;
// Store location of line
*current_line++ = (int)g - (int)current_line;
// Create gravity table
for (int x = -maxr + 1; x < maxr; x++)
{
double r = sqrt(square(x) + square(y));
if (r == 0 || r > maxr - 1)
offset = 0;
else
{
// Compute displacement in x and y direction
double v = f(r / maxr, a, t), gx = v * x / r, gy = v * y / r;
// Round
gx = gx < 0? gx - 0.5 : gx + 0.5;
gy = gy < 0? gy - 0.5 : gy + 0.5;
// Clip
if (x + gx <= -maxr)
gx = -maxr + 1 - x;
else if (x + gx >= maxr)
gx = maxr - 1 - x;
if (y + gy <= -maxr)
gy = -maxr + 1 - y;
else if (y + gy >= maxr)
gy = maxr - 1 - y;
// Compute offset
offset = (int)gx + (int)gy * w;
ASSERT(offset == (short)offset);
}
if (offset == 0)
{
// No change for this pixel
if (non_zero)
{
// Non zero occured, increase trailing zeros
trail_zeros++;
// Store zero in case there are non zero offsets following
*g++ = offset;
writes++;
}
else
{
// Only zeros yet, increase number of zeros at start
lead_zeros++;
}
}
else if (!non_zero)
{
// First non zero offset, store number of starting zeros
*g++ = lead_zeros;
// Make room for the number of bytes to write
where_writes = g;
g++;
// Store first non zero offset
*g++ = offset;
writes++;
// Indicate that non zero was encountered
non_zero = TRUE;
}
else
{
// Offset is non zero and at least one non zero offset before,
// reset trailing zeros
trail_zeros = 0;
// Store value
*g++ = offset;
writes++;
}
}
if (writes != 0)
{
// Store number of bytes to write
*where_writes = writes - trail_zeros;
// Remove trailing zeros from this line
g -= trail_zeros;
}
else
{
// Indicate that nothing has to be written
*g++ = w;
}
}
// Store bytes written
n = (int)g - (int)gravity_map;
// Return buffer
return gravity_map;
}
static double f_ripple(double r, double a, double t)
{
return a * (1 - r) * cos(4.5 * PI * r + 2 * PI * t);
}
static double f_sphere(double r, double a, double t)
{
return -a * r / sqrt(1 - r * r);
}
void create_gravity()
{
short *g;
int n;
// Create ripple effect
for (int t = 0; t < 6; t++)
{
g = radial_gravity(150, 20, (double)t / 6, f_ripple, n);
cData::add_to_file("Gravity.dat", g, n, construct("RIPPLE%d", t + 1));
delete g;
}
// Create spheres
g = radial_gravity(40, 3, 0, f_sphere, n);
cData::add_to_file("Gravity.dat", g, n, "SPHERE40");
delete g;
g = radial_gravity(60, 5, 0, f_sphere, n);
cData::add_to_file("Gravity.dat", g, n, "SPHERE60");
delete g;
g = radial_gravity(80, 7, 0, f_sphere, n);
cData::add_to_file("Gravity.dat", g, n, "SPHERE80");
delete g;
g = radial_gravity(100, 10, 0, f_sphere, n);
cData::add_to_file("Gravity.dat", g, n, "SPHERE100");
delete g;
}
void gravity_blit(LPDIRECTDRAWSURFACE4 dest, RECT *destrect, int x, int y, short *gravity_map)
{
BYTE *surface_copy, *s, *d;
DDSURFACEDESC2 destsurf;
int rw, rh, w, h, max_x, max_y;
// Get size of rectangle
rw = destrect->right - destrect->left;
rh = destrect->bottom - destrect->top;
// Get size of image
w = *gravity_map++;
h = *gravity_map++;
ASSERT(rw >= w && rh >= h);
// Check if there will be anything to blit
if (x <= -w || x >= rw || y <= -h || y >= rh)
return;
// Compute distance from x to right of rectangle with maximum w
max_x = rw - x;
if (max_x > w)
max_x = w;
// Compute distance from y to bottom of rectangle with maximum h
max_y = rh - y;
if (max_y > h)
max_y = h;
// Allocate temporary space
surface_copy = new BYTE [w * h];
// Lock surface
destsurf.dwSize = sizeof(destsurf);
while (!draw_ok(dest->Lock(destrect, &destsurf, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, 0)));
// Fill surface_copy buffer entirely (if not with image data then fill with black)
s = (BYTE *)destsurf.lpSurface + (x > 0? x : 0) + y * destsurf.lPitch;
d = surface_copy;
for (int i = 0; i < h; i++)
{
if (i < -y || i >= max_y)
{
FillMemory(d, w, black);
}
else
{
if (x >= 0)
{
CopyMemory(d, s, max_x);
if (max_x < w)
FillMemory(d + max_x, w - max_x, black);
}
else // if (x < 0)
{
FillMemory(d, -x, black);
CopyMemory(d - x, s, w + x);
}
}
s += destsurf.lPitch;
d += w;
}
// Setup source and destination pointer
s = surface_copy + (x < 0? -x : 0) + (y < 0? -y * w : 0);
d = (BYTE *)destsurf.lpSurface + (x > 0? x : 0) + (y > 0? y * destsurf.lPitch : 0);
// Go to the right offset value in the gravity map
if (y < 0)
gravity_map = (short *)((int *)gravity_map - y);
// Setup some values required in the assembly part
int write_x_start = x < 0? -x : 0,
write_y = y < 0? h + y : max_y;
// Do the blit
__asm
{
mov esi, gravity_map
mov ebx, s
mov edi, d
mov ecx, write_y
blit_loop_y:
// Get start of this scanline in the gravity map
push esi
add esi, [esi]
// Get number of leading zeros
lodsw
cwde
// Check leading zeros >= max_x
cmp eax, max_x
jge line_empty
// Store registers
push ebx
push ecx
push edi
// Get pixels to skip
sub eax, write_x_start
jc skip_more_than_leading
// Case 1: There are less pixels to skip than the number of leading zeros
// Get the remaining number of pixels to be written in ecx
mov ecx, max_x
sub ecx, write_x_start
sub ecx, eax
jbe line_empty2
// Adjust pointers to first pixel that has to be written
add ebx, eax
add edi, eax
// Get number of pixels writable in this line
lodsw
cwde
// Take the least of the two counters
cmp eax, ecx
jge blit_loop_x
mov ecx, eax
jmp blit_loop_x
skip_more_than_leading:
// Case 2: There are more pixels to skip than the number of leading zeros
// Get the number of pixels that still have to be skipped in edx
neg eax
mov edx, eax
// Get number of pixels writable in this line
lodsw
cwde
// Get the remaining number of pixels to be written in ecx
mov ecx, eax
sub ecx, edx
jbe line_empty2
// Adjust the gravity map pointer
shl edx, 1
add esi, edx
// Enter loop
blit_loop_x:
// Get offset
lodsw
cwde
// Get pixel from offset location and store it to the current location
mov dl, [ebx + eax]
mov [edi], dl
// Goto next pixel
inc ebx
inc edi
loop blit_loop_x
line_empty2:
// Restore registers
pop edi
pop ecx
pop ebx
line_empty:
// Go to next line
add ebx, w
add edi, destsurf.lPitch
pop esi
add esi, 4
loop blit_loop_y
}
// Unlock surface
backbuffer->Unlock(destrect);
// Release temporary buffer
delete surface_copy;
}
static void write_all_gravity(cDisplayable *l)
{
if (!no_parallax && !inawin && !low_detail_level)
for (; l != 0; l = (cDisplayable *)l->next)
l->write_gravity();
}
void write_gravity()
{
write_all_gravity(weapons);
write_all_gravity(effects);
}